#include "mapdraw.h"
#include "in.h"
#include "inxml.h"
#include "parser.h"

static GString* tempParserString = NULL;
static INSTREAM* in = NULL;
static INSTREAM* cmdLine = NULL;

static PROGRESSSTRUCT* progBar = NULL;

static XMLPARSER xmlParser = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, XML_ERROR };

static SFLAG BasicOpen ();
static SFLAG BasicEnd ();
static SFLAG BasicError ();

static SFLAG OpenKMLTag ();
static SFLAG OnMapError ();

static SFLAG LinesFromMAPFile ();

static SFLAG IsMAPFile ();

static SFLAG ReadCoordinates (INSTREAM* from);

static LINESET* StartLineSet ();
static MAPPOINT* StartMapPoint (SREAL lat, SREAL lon);

static GList* actMap = NULL;

SFLAG ReadMapFile (gchar* name, PROGRESSSTRUCT* progress) {
 SFLAG toRet;

 toRet = FALSE;
 if (in == NULL)
   in = CreateInStream ();
 if (tempParserString == NULL)
   tempParserString = g_string_new (VOIDSTRING);
 if (cmdLine == NULL)
   cmdLine = CreateInStream ();
 SETREADINGMODE (ALL_LOWER);
 if (SetInStreamToFile (in, name) != TRUE) return FALSE;
 progBar = progress;
 progBar -> numBar1 = in -> len;
 progBar -> actBar1 = 0;
 actMap = NULL;
 if (IsXMLFile (in) == TRUE) {
   xmlParser . OpenTag = BasicOpen;
   xmlParser . EndTag = BasicEnd;
   xmlParser . OnError = BasicError;
   xmlParser . tempParserString = tempParserString;
   xmlParser . in = in;
   toRet = ReadXMLFile (&xmlParser);
   xmlParser . OpenTag = NULL;
   xmlParser . EndTag = NULL;
   xmlParser . OnError = NULL;
   xmlParser . tempParserString = NULL;
   xmlParser . in = NULL;
 }
 else if (IsMAPFile () == TRUE)
   toRet = LinesFromMAPFile ();
 else {
   ResetInStream (in);
   return FALSE;
 }
 ResetInStream (in);
 if (toRet != FALSE) {
   if (mapLines == NULL)
     mapLines = actMap;
   else
     PutAtListEnd (mapLines, actMap);
 }
 return toRet;
}

SFLAG BasicOpen () {
 progBar -> actBar1 = in -> actPos;
 UpdateOnCalculus ();
 if ((!g_strcmp0 (tempParserString -> str, "document")) || (!g_strcmp0 (tempParserString -> str, "kml"))) {
   if (xmlParser . tokenState & XML_WITH_PARAMS) {
     if (SkipParams (in) == XML_AUTO_CLOSE) {
       RemoveFromTokenList (&xmlParser);
       return TRUE;
     }
   }
   xmlParser . OpenTag = OpenKMLTag;
   xmlParser . EndTag = BasicEnd;
   xmlParser . OnError = OnMapError;
   xmlParser . fileHeader = g_strdup (tempParserString -> str);
 }
 else
   return IgnoreOpenTag (&xmlParser);
 return TRUE;
}

SFLAG BasicEnd () {
 return TRUE;
}

SFLAG BasicError () {
 return FALSE;
}

static GList* lastSet = NULL;
static GList* lastLink = NULL;
static LINESET* set = NULL;
static SCOUNTER numPoints = 0;

SFLAG OpenKMLTag () {
 GString* actToken;

 progBar -> actBar1 = in -> actPos;
 UpdateOnCalculus ();
 if (!g_strcmp0 (tempParserString -> str, "coordinates")) {
   actToken = GetPrevToken (&xmlParser);
   if (actToken == NULL) {
     errorMessage = g_strdup_printf ("\nUnexpected \'<coordinates>\' token\nFile %s, line %ld\n", in -> name, in -> line);
     return FALSE;
   }
   if (xmlParser . tokenState & XML_WITH_PARAMS) {
     if (SkipParams (in) == XML_AUTO_CLOSE) {
       RemoveFromTokenList (&xmlParser);
       return TRUE;
     }
   }
   if (g_strcmp0 (actToken -> str, "linestring") && (g_strcmp0 (actToken -> str, "linearring"))) return IgnoreOpenTag (&xmlParser);
   set = StartLineSet ();
   lastLink = NULL;
   numPoints = 0;
   ReadXMLContent (in, tempParserString, &xmlParser . tokenState);
   if (xmlParser . tokenState != XML_TOKEN_CONTENT) {
     errorMessage = g_strdup_printf ("\nBad \'<coordinates>\' token\nFile %s, line %ld\n", in -> name, in -> line);
     return FALSE;
   }
   SetInStreamToString (cmdLine, tempParserString);
   if (ReadCoordinates (cmdLine) != TRUE) {
     DestroyLineSet (set);
     set = NULL;
     lastLink = NULL;
     numPoints = 0;     errorMessage = g_strdup_printf ("\nBad \'<coordinates>\' token\nFile %s, line %ld\n", in -> name, in -> line);
     OnError ();
   }
   else {
     set -> numPoints = numPoints;
     lastSet = InsertListElement (lastSet, set);
     if (actMap == NULL)
       actMap = lastSet;
     set = NULL;
     lastLink = NULL;
     numPoints = 0;
   }
 }
 else
   return IgnoreOpenTag (&xmlParser);
 return TRUE;
}

SFLAG ReadCoordinates (INSTREAM* from) {
 SREAL addLon, addLat;
 MAPPOINT* point;

 for (;;) {
   progBar -> actBar1 = in -> actPos;
   UpdateOnCalculus ();
   if (from -> state == EOF_REACHED) break;
   if ((!g_ascii_isdigit (from -> nextChar)) && (from -> nextChar != '-')) return FALSE;
     ReadReal (from, &addLon);
   if (from -> nextChar == ',')
     ReadChar (from, NULL);
   SkipSpaces (from);
   if ((!g_ascii_isdigit (from -> nextChar)) && (from -> nextChar != '-')) return FALSE;
   ReadReal (from, &addLat);
   if (from -> nextChar == ',') {
     ReadChar (from, NULL);
     ReadString (from, NULL);
   }
   SkipSpaces (from);
   if ((addLon > -180.00001) && (addLon < 180.00001) && (addLat > -90.00001) && (addLat < 90.00001)) {
     point = StartMapPoint (addLat, addLon);
     lastLink = InsertListElement (lastLink, point);
     if (set -> mapPoints == NULL)
       set -> mapPoints = lastLink;
     ++ numPoints;
   }
   else {
     errorMessage = g_strdup_printf ("\nBad \'<coordinates>\' token\nFile %s, line %ld\n", in -> name, in -> line);
     OnError ();
     return FALSE;
   }
 }
 return TRUE;
}

SFLAG LinesFromMAPFile () {
 MAPPOINT* point;
 SREAL addLon, addLat;

 lastSet = NULL;
 for (;;) {
   progBar -> actBar1 = in -> actPos;
   UpdateOnCalculus ();
   if (ReadString (in, tempParserString) == EOF_REACHED) break;
   if (g_strcmp0 (tempParserString -> str, "line")) {
     errorMessage = g_strdup_printf ("Unknown expression %s\nExpecting \'line\' keyword\nFile: %s, line: %ld",
                    tempParserString -> str, in -> name, in -> line);
     return OnMapError ();
   }
   set = StartLineSet ();
   lastLink = NULL;
   numPoints = 0;
   for (;;) {
     progBar -> actBar1 = in -> actPos;
     UpdateOnCalculus ();
     if (in -> state == EOF_REACHED) break;
     if ((in -> nextChar == 'l') || (in -> nextChar == 'L')) break;
     addLat = addLon = 360.00;
     if (ReadReal (in, &addLon) == BAD_IN_FORMAT) {
       errorMessage = g_strdup_printf ("Expecting number\nFile: %s, line: %ld", in -> name, in -> line);
       return OnMapError ();
     }
     if (in -> state == EOF_REACHED) {
       errorMessage = g_strdup_printf ("Unexpected End of File\nFile: %s", in -> name);
       return OnMapError ();
     }
     if (ReadReal (in, &addLat) == BAD_IN_FORMAT) {
       errorMessage = g_strdup_printf ("Expecting number\nFile: %s, line: %ld", in -> name, in -> line);
       return OnMapError ();
     }
     if ((addLon > -180.00001) && (addLon < 180.00001) && (addLat > -90.00001) && (addLat < 90.00001)) {
       point = StartMapPoint (addLat, addLon);
       lastLink = InsertListElement (lastLink, point);
       if (set -> mapPoints == NULL)
         set -> mapPoints = lastLink;
       ++ numPoints;
     }
   }
   set -> numPoints = numPoints;
   lastSet = InsertListElement (lastSet, set);
   if (actMap == NULL)
     actMap = lastSet;
 }
 return TRUE;
}

SFLAG OnMapError () {
 OnError ();
 return FALSE;
}

SFLAG IsMAPFile () {
 SkipSpaces (in);
 if (ReadString (in, tempParserString) == EOF_REACHED) {
   in -> line = 0;
   in -> actPos = 0;
   SkipSpaces (in);
   return FALSE;
 }
 if (!g_strcmp0 (tempParserString -> str, "map")) return TRUE;
 in -> line = 0;
 in -> actPos = 0;
 SkipSpaces (in);
 return FALSE;
}

LINESET* StartLineSet () {
 LINESET* toRet;

 toRet = g_slice_alloc (sizeof (LINESET));
 toRet -> numPoints = 0;
 toRet -> mapPoints = NULL;
 toRet -> drawPoints = NULL;
 toRet -> line = OTHERLINE;
 return toRet;
}

MAPPOINT* StartMapPoint (SREAL lat, SREAL lon) {
 MAPPOINT* toRet;

 toRet = g_slice_alloc (sizeof (MAPPOINT));
 toRet -> inclination = FROMLATTOINC (lat);
 toRet -> azimuth = FROMLONTOAZM (lon);
 toRet -> x = toRet -> azimuth;
 toRet -> y = toRet -> inclination;
 return toRet;
}

void DestroyLineSet (LINESET* toDel) {
 DestroyList (toDel -> mapPoints, sizeof (MAPPOINT));
 if (toDel -> drawPoints != NULL)
   g_free (toDel -> drawPoints);
 g_slice_free1 (sizeof (LINESET), toDel);
}
